package com.peaksport.framework.uuid;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * 实体类型
 * 有8位十六进制字符组成,由改实体类型生产的实体主键id是全球唯一的,并且可逆成对应的entityType
 * 
 * @author jinbin_guo  2019年1月5日 下午4:33:02
 *
 */
public class PKEntityType implements Serializable {
    private static final long serialVersionUID = -65534L;
    private transient String strType;
    private transient int intType;
    private static Map<String, PKEntityType> types;

    private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };


    private PKEntityType(int type) {
        if (type == 0) {
            throw new IllegalArgumentException("zero PKEntityType.");
        }
        this.intType = type;
        this.strType = null;
    }

    private PKEntityType(String type) {
        if (type == null) {
            throw new IllegalArgumentException("null PKEntityType.");
        }
        type = type.trim();
        if (type.length() == 4) {
            this.intType = 0;
            this.strType = type.intern();
        } else {
            assert (type.length() == 8);
            long parsedLong = 0L;
            try {
                parsedLong = Long.parseLong(type, 16);

                if (parsedLong > 2147483647L) {
                    parsedLong = -2147483648L + parsedLong - 2147483647L - 1L;
                }
            } catch (NumberFormatException nfe) {
            }

            this.intType = (int) parsedLong;
            if (parsedLong == 0L) {
                this.strType = type;
            } else
                this.strType = null;
        }
    }

    public static PKEntityType create(String type) {
        PKEntityType val = (PKEntityType) types.get(type);
        if (val != null) {
            return val;
        }
        synchronized (types) {
            val = (PKEntityType) types.get(type);
            if (val == null) {
                val = new PKEntityType(type);
                types.put(type, val);
            }
        }
        return val;
    }

    public String toString() {
        if (this.strType != null) {
            assert (this.intType == 0);
            return this.strType;
        }

        assert (this.intType != 0);
        return toHexString(this.intType, 8).toUpperCase();
    }

    private  String toHexString(long x, int chars)  {
        char[] buf = new char[chars];
        for (int charPos = chars; --charPos >= 0; ) {
            buf[charPos] = hexDigits[(int)(x & 0xF)];
            x >>>= 4;
        }
        return new String(buf);
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if ((obj != null) && (obj instanceof PKEntityType)) {
            if (this.strType != null) {
                return this.strType
                        .equalsIgnoreCase(((PKEntityType) obj).strType);
            }
            return (this.intType == ((PKEntityType) obj).intType);
        }
        return false;
    }

    public int hashCode() {
        if (this.strType != null) {
            return this.strType.hashCode();
        }
        return this.intType;
    }

    public void write(DataOutput out) throws IOException {
        out.writeInt(this.intType);
        if (this.intType != 0)
            return;
        out.writeUTF(this.strType);
    }

    public static PKEntityType read(DataInput in) throws IOException {
        int intType = in.readInt();
        if (intType != 0) {
            return create(new PKEntityType(intType).toString());
        }

        String strType = in.readUTF();
        return create(strType);
    }

    private void writeObject(ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        s.writeInt(this.intType);
        if (this.intType == 0)
            s.writeObject(this.strType);
    }

    private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
        s.defaultReadObject();

        this.intType = s.readInt();
        if (this.intType == 0)
            this.strType = ((String) s.readObject()).intern();
    }

    private Object readResolve() throws ObjectStreamException {
        return create(toString());
    }

    public int toInteger() {
        return this.intType;
    }

    static {
        types = new HashMap<>();
    }

}
